package cn.demoncat.util.lang;

import cn.demoncat.util.exception.ReflexRuntimeException;
import org.reflections.ReflectionUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.function.Predicate;

/**
 * 反射Field工具
 * 
 * @author 延晓磊
 *
 * @since 2019年11月25日
 */
public class ReflectFieldUtil {

	// SerialVersion字段名
	private static final String SERIAL_VERSION_UID_FIELD = "serialVersionUID";
	// 获取字段失败
	private static final String FIELD_ACCESS_ERROR = "反射访问Field失败：field = {}";
		
	/**
	 * 获取对象的字段值
	 * 
	 * @param obj		对象
	 * @param field		字段
	 * 
	 * @return 对象的字段值
	 * 
	 * @exception ReflexRuntimeException	获取失败
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年11月25日
	 */
	public static Object get(Object obj, Field field) {
		// 授权访问
		if (!field.isAccessible()) {
			field.setAccessible(true);
		}
		// 获取字段值
		try {
			return field.get(obj);
		} catch (IllegalArgumentException | IllegalAccessException e) {
			throw new ReflexRuntimeException(StringUtil.logFormat(FIELD_ACCESS_ERROR, field), e);
		}
	}
	
	/**
	 * 获取对象的字段值
	 * 
	 * @param obj		对象
	 * @param fieldName	字段名
	 * 
	 * @return 对象的字段值
	 * 
	 * @exception ReflexRuntimeException	获取失败
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年11月25日
	 */
	public static Object get(Object obj, String fieldName) {
		// 获取字段
		Field field = getField(obj.getClass(), fieldName);
		// 获取值
		return field == null ? null : get(obj, field);
	}
	
	/**
	 * 设置对象的字段值(直接设置)
	 * 
	 * @param obj		对象
	 * @param field		字段
	 * @param value		值
	 * 
	 * @return 对象的字段值
	 * 
	 * @exception ReflexRuntimeException	设置失败
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年11月25日
	 */
	public static void set(Object obj, Field field, Object value) {
		// 授权访问
		if (!field.isAccessible()) {
			field.setAccessible(true);
		}
		// 设置字段值
		try {
			field.set(obj, value);
		} catch (IllegalArgumentException | IllegalAccessException e) {
			throw new ReflexRuntimeException(StringUtil.logFormat(FIELD_ACCESS_ERROR, field), e);
		}
	}
 
	/**
	 * 设置对象的字段值（忽略final,static,类型不匹配字段）
	 * 
	 * @param obj		对象
	 * @param field		字段
	 * @param value		值
	 * @param excludeNotNull	是否排除非null值：true 字段非null时忽略
	 * 
	 * @return 对象的字段值
	 * 
	 * @exception ReflexRuntimeException	设置失败
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年11月25日
	 */
	public static void set(Object obj, Field field, Object value, boolean excludeNotNull) {
		set(obj, field, value, true, excludeNotNull);
	}
	
	/**
	 * 设置对象的字段值（忽略final,类型不匹配字段）
	 * 
	 * @param obj		对象
	 * @param field		字段
	 * @param value		值
	 * @param excludeStatic		是否排除static字段
	 * @param excludeNotNull	是否排除非null值：true 字段非null时忽略
	 * 
	 * @return 对象的字段值
	 * 
	 * @exception ReflexRuntimeException	设置失败
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年11月25日
	 */
	public static void set(Object obj, Field field, Object value, boolean excludeStatic, boolean excludeNotNull) {
		// 忽略 final, static, 类型不匹配
		if (Modifier.isFinal(field.getModifiers()) || (excludeStatic && Modifier.isStatic(field.getModifiers())) || (value != null && !field.getType().equals(value.getClass()))) {
			return;
		}
		// 忽略非null值
		if (excludeNotNull && get(obj, field) != null) {
			return;
		}
		// 设置
		set(obj, field, value);
	}
	
	/**
	 * 设置对象的字段值（忽略final,类型不匹配字段）
	 * 
	 * @param obj		对象
	 * @param fieldName	字段名
	 * @param value		值
	 * 
	 * @return 对象的字段值
	 * 
	 * @exception ReflexRuntimeException	设置失败
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年11月25日
	 */
	public static void set(Object obj, String fieldName, Object value) {
		set(obj, fieldName, value, false, false);
	}
	
	/**
	 * 设置对象的字段值（忽略final，static,类型不匹配字段）
	 * 
	 * @param obj		对象
	 * @param fieldName	字段名
	 * @param value		值
	 * @param excludeNotNull	是否排除非null值：true 字段非null时忽略
	 * 
	 * @return 对象的字段值
	 * 
	 * @exception ReflexRuntimeException	设置失败
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年11月25日
	 */
	public static void set(Object obj, String fieldName, Object value, boolean excludeNotNull) {
		set(obj, fieldName, value, true, excludeNotNull);
	}
	
	/**
	 * 设置对象的字段值（忽略final,类型不匹配字段）
	 * 
	 * @param obj		对象
	 * @param fieldName	字段名
	 * @param value		值
	 * @param excludeStatic		是否排除static字段
	 * @param excludeNotNull	是否排除非null值：true 字段非null时忽略
	 * 
	 * @return 对象的字段值
	 * 
	 * @exception ReflexRuntimeException	设置失败
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年11月25日
	 */
	public static void set(Object obj, String fieldName, Object value, boolean excludeStatic, boolean excludeNotNull) {
		// 获取字段
		Field field = getField(obj.getClass(), fieldName);
		// 设置值
		if (field != null) {
			set(obj, field, value, excludeStatic, excludeNotNull);
		}
	}

	/**
	 * 设置对象的字段值(直接设置)
	 *
	 * @param obj		对象
	 * @param fields	字段:值
	 *
	 * @return
	 *
	 * @exception ReflexRuntimeException	设置失败
	 *
	 * @author 延晓磊
	 *
	 * @since 2019年11月25日
	 */
	public static void set(Object obj, Map<String,Object> fields) {
		for (Map.Entry<String, Object> entry : fields.entrySet()) {
		    set(obj, entry.getKey(), entry.getValue());
		}
	}

	/**
	 * 获取指定字段
	 * 
	 * <pre>
	 * 1、包含继承、私有字段
	 * 2、同名字段时，忽略继承字段
	 * 3、所有字段授权访问
	 * 4、忽略serialVersionUID字段
	 * </pre>
	 * 
	 * @param clazz
	 * @param fieldName
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年9月25日
	 */
	public static Field getField(Class<?> clazz, String fieldName) {
		Class<?> current = clazz;
		Field declaredField = null;
        while (current != null) {
        	// 获取类声明的字段：全部字段，不包含继承的字段
        	try {
				declaredField = current.getDeclaredField(fieldName);
			} catch (NoSuchFieldException | SecurityException e) {
				// 未找到
			}
        	// 如果不存在，寻找继承字段
        	if (declaredField == null) {
        		current = current.getSuperclass();
			}else {
				// 授权访问
     			if (!declaredField.isAccessible()) {
     				declaredField.setAccessible(true);
				}
     			break;
			}
        }
        return declaredField;
	}
	
	/**
	 * 获取全部字段
	 * 
	 * <pre>
	 * 1、包含继承、私有字段
	 * 2、同名字段时，忽略继承字段
	 * 3、所有字段授权访问
	 * 4、忽略serialVersionUID字段
	 * </pre>
	 * 
	 * @param clazz
	 * @param ignoreStatic	是否忽略static字段
	 * @param ignoreFinal	是否忽略final字段
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年9月25日
	 */
	public static List<Field> getFields(Class<?> clazz, boolean ignoreStatic, boolean ignoreFinal) {
		List<Field> fields = new ArrayList<>();
		Set<String> fieldNames = new HashSet<>();
		Class<?> current = clazz;
		Field[] declaredFields;
        while (current != null) {
        	// 获取类声明的字段：全部字段，不包含继承的字段
         	declaredFields = current.getDeclaredFields();
         	for (Field field : declaredFields) {
         		// 忽略SerialVersionUid
         		if (SERIAL_VERSION_UID_FIELD.equals(field.getName())) {
					continue;
				}
         		// 忽略static字段
         		if (ignoreStatic && Modifier.isStatic(field.getModifiers())) {
					continue;
				}
         		// 忽略final字段
         		if (ignoreFinal && Modifier.isFinal(field.getModifiers())) {
					continue;
				}
				// 有同名字段时，忽略继承字段
         		if (fieldNames.add(field.getName())) {
					// 授权访问
         			if (!field.isAccessible()) {
						field.setAccessible(true);
					}
         			fields.add(field);
				}
			}
         	// 获取继承的声明字段
         	current = current.getSuperclass();
        }
        return fields;
	}
	
	/**
	 * 获取全部字段名
	 * 
	 * <pre>
	 * 1、包含继承、私有字段
	 * 2、同名字段时，忽略继承字段
	 * 3、忽略serialVersionUID字段
	 * </pre>
	 * 
	 * @param clazz
	 * @param ignoreStatic	是否忽略static字段
	 * @param ignoreFinal	是否忽略final字段
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年9月25日
	 */
	public static Set<String> getFieldNames(Class<?> clazz, boolean ignoreStatic, boolean ignoreFinal) {
		// 获取全部字段
		Set<String> fieldNames = new HashSet<>();
		Class<?> current = clazz;
		Field[] declaredFields;
        while (current != null) {
        	// 获取类声明的字段：全部字段，不包含继承的字段
         	declaredFields = current.getDeclaredFields();
         	for (Field field : declaredFields) {
         		// 忽略SerialVersionUid
         		if (SERIAL_VERSION_UID_FIELD.equals(field.getName())) {
					continue;
				}
         		// 忽略static字段
         		if (ignoreStatic && Modifier.isStatic(field.getModifiers())) {
					continue;
				}
         		// 忽略final字段
         		if (ignoreFinal && Modifier.isFinal(field.getModifiers())) {
					continue;
				}
         		fieldNames.add(field.getName());
			}
         	// 获取继承的声明字段
         	current = current.getSuperclass();
        }
        return fieldNames;
	}
	
	/**
	 * 获取全部字段
	 * 
	 * <pre>
	 * 1、包含继承、私有字段，包含static字段，包含final字段
	 * 2、同名字段时，忽略继承字段
	 * 3、所有字段授权访问
	 * 4、忽略serialVersionUID字段
	 * </pre>
	 * 
	 * @param clazz
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年9月25日
	 */
	public static List<Field> getFields(Class<?> clazz) {
        return getFields(clazz, false, false);
	}
	
	/**
	 * 获取全部字段名
	 * 
	 * <pre>
	 * 1、包含继承、私有字段，包含static字段，包含final字段
	 * 2、同名字段时，忽略继承字段
	 * 3、忽略serialVersionUID字段
	 * </pre>
	 * 
	 * @param clazz
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年9月25日
	 */
	public static Set<String> getFieldNames(Class<?> clazz) {
		return getFieldNames(clazz, false, false);
	}
	
	/**
	 * 获取全部实例字段
	 * 
	 * <pre>
	 * 1、包含继承、私有字段，不包含static字段，包含final字段
	 * 2、同名字段时，忽略继承字段
	 * 3、所有字段授权访问
	 * 4、忽略serialVersionUID字段
	 * </pre>
	 * 
	 * @param clazz
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年9月25日
	 */
	public static List<Field> getInstanceFields(Class<?> clazz) {
        return getFields(clazz, true, false);
	}
	
	/**
	 * 获取全部实例字段名
	 * 
	 * <pre>
	 * 1、包含继承、私有字段，不包含static字段，包含final字段
	 * 2、同名字段时，忽略继承字段
	 * 3、忽略serialVersionUID字段
	 * </pre>
	 * 
	 * @param clazz
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年9月25日
	 */
	public static Set<String> getInstanceNames(Class<?> clazz) {
        return getFieldNames(clazz, true, false);
	}
	
	/**
	 * 获取全部实例非final字段
	 * 
	 * <pre>
	 * 1、包含继承、私有字段，不包含static字段，不包含final字段
	 * 2、同名字段时，忽略继承字段
	 * 3、所有字段授权访问
	 * 4、忽略serialVersionUID字段
	 * </pre>
	 * 
	 * @param clazz
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年9月25日
	 */
	public static List<Field> getInstanceWritableFields(Class<?> clazz) {
        return getFields(clazz, true, true);
	}
	
	/**
	 * 获取全部实例非final字段名
	 * 
	 * <pre>
	 * 1、包含继承、私有字段，不包含static字段，不包含final字段
	 * 2、同名字段时，忽略继承字段
	 * 3、忽略serialVersionUID字段
	 * </pre>
	 * 
	 * @param clazz
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年9月25日
	 */
	public static Set<String> getInstanceWritableNames(Class<?> clazz) {
        return getFieldNames(clazz, true, true);
	}
	
	/**
	 * 获取声明字段，不包含继承字段
	 * 
	 * @param type			类型
	 * @param predicates	筛选条件
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年2月1日
	 */
    @SuppressWarnings("unchecked")
	public static Set<Field> getFields(final Class<?> type, Predicate<? super Field> predicates) {
       return ReflectionUtils.getFields(type, predicates);
    }
    
	/**
	 * 获取全部字段 ，包含继承字段
	 * 
	 * @param type			类型
	 * @param predicates	筛选条件
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年2月1日
	 */
	@SuppressWarnings("unchecked")
	public static Set<Field> getAllFields(final Class<?> type, Predicate<? super Field> predicates) {
       return ReflectionUtils.getAllFields(type, predicates);
    }
}
